总结 CSS 变量的基本用法和最佳实践
变量声明
:root {
  --foo: 10px;
  --bar: 1px solid black;
}
上面的代码中,在根元素选择器内声明了两个变量:--foo 和 --bar。
它们和普通的 CSS 属性 font、border 没有任何区别,因此 CSS 变量也称为 CSS 自定义属性。
但是 CSS 自定义属性并不会指代什么,没有默认含义,比如 font 属性指代了字体,但是这里的 --foo 和 --bar 并没有实际意义,它们的唯一作用就是:用来被赋值给其他 CSS 属性。
各种值都可以存放在 CSS 变量中:
:root{
  --main-color: #4d4e53;
  --main-bg: rgb(255, 255, 255);
  --logo-border-color: rebeccapurple;
  --header-height: 68px;
  --content-padding: 10px 20px;
  --base-line-height: 1.428571429;
  --transition-duration: .35s;
  --external-link: "external link";
  --margin-top: calc(2vh + 20px);
}
需要注意的是,变量名大小写敏感,--header-color 和 --Header-Color 是两个不同变量。
var() 函数
var() 函数用于读取变量。
:root {
  --foo: skyblue;
  --bar: orange;
}
a {
  color: var(--foo); // 等价于 color: skyblue
  text-decoration-color: var(--bar); // 等价于 text-decoration-color: orange
}
var() 函数还可以使用第二个参数,表示变量的默认值。如果该变量不存在就会使用默认值。
color: var(--foo, #7F583F);
第二个参数不处理内部的逗号或空格,都视作参数的一部分。
var(--font-stack, "Roboto", "Helvetica");
var(--pad, 10px 15px 20px);
var() 函数也可以用在变量声明的地方。
:root {
  --primary-color: red;
  --logo-text: var(--primary-color);
}
var() 函数也可以进行嵌套使用。
.two {
  color: var(--my-var, red); /* Red if --my-var is not defined */
}
.three {
  background-color: var(--my-var, var(--my-background, pink)); /* pink if --my-var and --my-background are not defined */
}
变量值类型
如果变量值是字符串,那么可以与其他字符串拼接。
--bar: 'hello';
--foo: var(--bar)' world';
如果变量值是数值,不能与 数值单位 直接连用。
.foo {
  --gap: 20;
  /* 无效 */
  margin-top: var(--gap)px;
}
需要搭配 calc函数 使用。
.foo {
  --gap: 20;
  margin-top: calc(var(--gap) * 1px);
}
如果变量值带有单位,就不能写成字符串。
/* 无效 */
.foo {
  --foo: '20px';
  font-size: var(--foo);
}
/* 有效 */
.foo {
  --foo: 20px;
  font-size: var(--foo);
}
变量的作用域
同一个 CSS 变量,可以在多个选择器内声明。读取的时候,优先级最高的声明生效。这与 CSS 的"层叠"(cascade)规则是一致的。还需要注意的是 CSS 变量默认具有 继承性,比如我们在根选择器内定义了一个变量,那么内部的所有元素都继承了该 CSS 变量。
看看下面的 例子:
<style>
  :root {
    --font-color: skyblue;
  }
  
  .father {
    color: var(--font-color);
  }
  
  .child1 {
    --font-color: red;
    /* 需要重新给 color 赋值,覆盖从 .father 继承过来的 color */
    color: var(--font-color);
  }
  
  .child2 {
    /* 这里的 --font-color 是来自 :root 中声明的变量 */
    color: var(--font-color);
  }
</style>
<div class="father">
  <div class="child1">child1</div>
  <div class="child2">child2</div>
</div>
另外,需要注意的是,当浏览器遇到无效的 var() 时,会使用属性的继承值或初始值代替。
最佳实践
JavaScript 操作 CSS 变量
可以使用下面的方式来增删、读写 CSS 变量:
// 设置变量
document.body.style.setProperty('--primary', '#7F583F');
// 读取变量
document.body.style.getPropertyValue('--primary').trim(); // '#7F583F'
// 删除变量
document.body.style.removeProperty('--primary');
下面来看一个 例子:
<style>
  :root {
    --primary-background: orange;
    --primary-height: 100px;
  }
  
  div {
    width: 300px;
    height: var(--primary-height);
    margin: 100px auto;
    border: solid;
    background: var(--primary-background);
  }
</style>
<div>
  <button class='change-background-btn'>ChangeBackground</button>
  <button class='change-height-btn'>ChangeHeight</button>
</div>
<script>
  const btn1 = document.querySelector(".change-background-btn");
  const btn2 = document.querySelector(".change-height-btn");
  // 点击第一个按钮,修改 --primary-background 变量值
  btn1.addEventListener("click", () => {
    document.documentElement.style.setProperty("--primary-background", "#7F583F");
  });
  // 点击第二个按钮,修改 --primary-height 变量值
  btn2.addEventListener("click", () => {
    document.documentElement.style.setProperty("--primary-height", "200px");
  });
</script>
通过这种方式可以很容易的来修改全局的主题色,当然前提是所有的组件使用的都是相关的 CSS 变量。
结合媒体查询
CSS 变量与媒体查询结合,可以使得不同的屏幕宽度有不同的变量值。
body {
  --primary: #7F583F;
  --secondary: #F7EFD2;
}
a {
  color: var(--primary);
  text-decoration-color: var(--secondary);
}
@media screen and (min-width: 768px) {
  body {
    --primary:  #F7EFD2;
    --secondary: #7F583F;
  }
}